package luj.tool.jnproxyan.proxy.network.direct;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import luj.cluster.api.actor.Tellable;
import luj.net.internal.client.connect.ClientBootMaker;
import luj.tool.jnproxyan.proxy.network.logger.Proxy2sLogger;
import luj.tool.jnproxyan.proxy.network.socks.actor.root.message.request.connect.finish.ConnectFinishMsg;
import luj.tool.jnproxyan.proxy.network.direct.state.P2sConn;
import luj.tool.jnproxyan.proxy.network.direct.state.P2sConnAttr;
import luj.tool.jnproxyan.framework.proxy.actor.tell.ActorTeller;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class HttpsDirectConnector {

  public HttpsDirectConnector inject(EventLoopGroup loop, String host, int port, Tellable socksRef, Long reqId) {
    var result = new HttpsDirectConnector();
    result._loop = loop;
    result._host = host;
    result._port = port;

    result._socksRef = socksRef;
    result._reqId = reqId;

    result._directHandler = _directHandler;
    result._actorTeller = _actorTeller;

    return result;
  }

  /**
   * @see luj.tool.jnproxyan.proxy.network.socks.actor.root.message.request.connect.finish.OnConnectFinish#onHandle
   */
  public void connect() throws Exception {
    Bootstrap bootstrap = new ClientBootMaker(_loop).make();
    bootstrap.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000);

    bootstrap.handler(new ChannelInitializer<SocketChannel>() {
      @Override
      protected void initChannel(SocketChannel ch) {
        var state = new P2sConn(_reqId);
        new P2sConnAttr(ch).init(state);

        ch.pipeline().addLast(_directHandler.inject(_socksRef));
      }
    });

    var p2sLog = new Proxy2sLogger(LOG, _reqId);
    p2sLog.log("开始连<站>：{}:{}", _host, _port);

    bootstrap.connect(_host, _port).addListener(new ChannelFutureListener() {
      @Override
      public void operationComplete(ChannelFuture future) {
        _actorTeller.inject(_socksRef).tell(ConnectFinishMsg.class, (b, m) -> b
            .f(m::reqId).set(_reqId)
            .f(m::targetHost).set(_host)
            .f(m::targetPort).set(_port)
            .f(m::connectResult2).set(future)
        );
      }
    });
  }

  private static final Logger LOG = LoggerFactory.getLogger(HttpsDirectConnector.class);

  EventLoopGroup _loop;
  String _host;
  int _port;

  Tellable _socksRef;
  Long _reqId;

  @Autowired
  DirectHandler _directHandler;

  @Autowired
  ActorTeller _actorTeller;
}
